/*
 * Copyright (C) 2009 M. Hautle.
 * 
 * This file is part of TrackedBean.
 * 
 * TrackedBean is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General
 * Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
 * later version.
 * 
 * TrackedBean is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU Lesser General Public License along with TrackedBean. If not, see
 * <http://www.gnu.org/licenses/>.
 */
package ch.trackedbean.binding.mapping.lists;

import java.util.*;
import javax.swing.*;
import ch.trackedbean.loaders.lists.*;

/**
 * {@link javax.swing.ListModel} which uses the {@link ListManager} to obtain it's content.
 * 
 * @see ListManager
 * @author M. Hautle
 * @param <T> The content type
 */
public class ListManagerModel<T> extends AbstractListModel {
	/** The content type of the list. */
	private final Class<T> type;

	/** Additional string specifying the "subtype" of the lists content (may be null). */
	private String subType;

	/** Additional settings for the list content load (may be null). */
	private Map<Object, Object> settings;

	/** The current list. */
	private List<T> list = Collections.emptyList();

	/**
	 * Default constructor.
	 * 
	 * @param type The content type of the list
	 * @throws IllegalStateException if the group is already bound
	 */
	public ListManagerModel(Class<T> type) {
		this(type, null, null, true);
	}

	/**
	 * Default constructor.
	 * 
	 * @param type The content type of the list
	 * @param subType Additional string specifying the "subtype" of the lists content (may be null)
	 */
	public ListManagerModel(Class<T> type, String subType) {
		this(type, subType, null, true);
	}

	/**
	 * Default constructor.
	 * 
	 * @param type The content type of the list
	 * @param subType Additional string specifying the "subtype" of the lists content (may be null)
	 * @param settings Additional settings for the list content load (may be null)
	 * @param load True if the content should be loaded now (if true {@link #refresh()} will be called)
	 */
	public ListManagerModel(Class<T> type, String subType, Map<Object, Object> settings, boolean load) {
		this.type = type;
		this.subType = subType;
		this.settings = settings;
		if (load)
			refresh();
	}

	/**
	 * Refreshes the list.<br>
	 * Calls simply {@link ListManager#getList(Class, String, Map)}.
	 */
	public void refresh() {
		refresh(false);
	}

	/**
	 * Refreshes the list.<br>
	 * Calls simply {@link ListManager#getList(Class, String, Map)} or {@link ListManager#loadList(Class, String, Map)}
	 * if <code>foreceLoad</code> is specified.
	 * 
	 * @param forceLoad True to force a load even when the list exists in the chache
	 */
	public void refresh(boolean forceLoad) {
		if (forceLoad)
			list = ListManager.ME.loadList(type, subType, settings);
		else
			list = ListManager.ME.getList(type, subType, settings);
		fireContentsChanged(this, -1, -1);
	}

	/**
	 * Clears the list.
	 */
	@SuppressWarnings("unchecked")
	public void clear() {
		list = Collections.EMPTY_LIST;
		fireContentsChanged(this, -1, -1);
	}

	/**
	 * Removes the given elements from the model.
	 * 
	 * @param c The collection containing the elements to remove
	 * @see java.util.List#removeAll(java.util.Collection)
	 */
	public void removeAll(Collection<?> c) {
		list.removeAll(c);
		fireContentsChanged(this, -1, -1);
	}

	/**
	 * Removes all other elements from the model.
	 * 
	 * @param c The collection containing the elements to be retained
	 * @see java.util.List#retainAll(java.util.Collection)
	 */
	public void retainAll(Collection<?> c) {
		list.retainAll(c);
		fireContentsChanged(this, -1, -1);
	}

	/**
	 * Adds the given element at the specified index.
	 * 
	 * @param index The index
	 * @param element The element
	 * @see java.util.List#add(int, java.lang.Object)
	 */
	public void add(int index, T element) {
		list.add(index, element);
		fireIntervalAdded(this, index, index);
	}

	/**
	 * Adds the given element.
	 * 
	 * @param e The element
	 * @see java.util.List#add(java.lang.Object)
	 */
	public void add(T e) {
		final int size = getSize();
		list.add(e);
		fireIntervalAdded(this, size, size);
	}

	/**
	 * Adds the content of the given collection to the model.
	 * 
	 * @param c The collection holding the elements to add
	 * @see java.util.List#addAll(java.util.Collection)
	 */
	public void addAll(Collection<? extends T> c) {
		list.addAll(c);
		fireContentsChanged(this, -1, -1);
	}

	/**
	 * Adds the content of the given collection to the model, the first element of the collection will be inserted at
	 * the given index.
	 * 
	 * @param index The index
	 * @param c The collection holding the elements to add
	 * @see java.util.List#addAll(int, java.util.Collection)
	 */
	public void addAll(int index, Collection<? extends T> c) {
		list.addAll(index, c);
		fireContentsChanged(this, -1, -1);
	}

	/**
	 * Checks if the model contains the given element.
	 * 
	 * @param o The object
	 * @return True if the model contains it
	 * @see java.util.List#contains(java.lang.Object)
	 */
	public boolean contains(Object o) {
		return list.contains(o);
	}

	/**
	 * Returns the index of the given object.
	 * 
	 * @param o The object
	 * @return The index or -1 if the element doesen't exists
	 * @see java.util.List#indexOf(java.lang.Object)
	 */
	public int indexOf(Object o) {
		return list.indexOf(o);
	}

	/**
	 * Removes the element at the given index.
	 * 
	 * @param index The index
	 * @see java.util.List#remove(int)
	 */
	public void remove(int index) {
		list.remove(index);
		fireIntervalRemoved(this, index, index);
	}

	/**
	 * Removes the given object
	 * 
	 * @param o The object to remove
	 * @see java.util.List#remove(java.lang.Object)
	 */
	public void remove(Object o) {
		final int index = list.indexOf(o);
		if (index > -1)
			remove(index);
	}

	/**
	 * Sets the element at the given index.
	 * 
	 * @param index The index
	 * @param element The element
	 */
	public void set(int index, T element) {
		list.set(index, element);
		fireContentsChanged(this, index, index);
	}

	/**
	 * @return Returns the subType.
	 */
	public String getSubType() {
		return subType;
	}

	/**
	 * @param subType The subType to set.
	 */
	public void setSubType(String subType) {
		this.subType = subType;
	}

	/**
	 * @return Returns the settings.
	 */
	public Map<Object, Object> getSettings() {
		return settings;
	}

	/**
	 * @param settings The settings to set.
	 */
	public void setSettings(Map<Object, Object> settings) {
		this.settings = settings;
	}

	/**
	 * @return Returns the type.
	 */
	public Class<T> getType() {
		return type;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Object getElementAt(int index) {
		return list.get(index);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public int getSize() {
		return list.size();
	}
}
